package com.miaomiao.service.system.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.IdUtil;
import com.miaomiao.common.RedisPrefix;
import com.miaomiao.common.Result;
import com.miaomiao.domain.system.LoginBody;
import com.miaomiao.domain.system.LoginUser;
import com.miaomiao.domain.system.Menu;
import com.miaomiao.domain.system.Router;
import com.miaomiao.service.system.LoginService;
import com.miaomiao.service.system.MenuService;
import com.miaomiao.utils.JwtUtil;
import com.miaomiao.utils.RedisCache;
import com.miaomiao.utils.RsaUtils;
import com.miaomiao.utils.SecurityUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MenuService menuService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private RedisCache redisCache;

    /**
     * 登录验证
     * @param loginBody 登录信息
     * @return
     */
    @Override
    public Result login(LoginBody loginBody) {
        //验证码校验
        validateCaptcha(loginBody.getCode(),loginBody.getUuid());
        try {
            loginBody.setPassword(RsaUtils.decryptByPrivateKey(loginBody.getPassword()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("登录失败");
        }
        //AuthenticationManager authenticate进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = null;
        if(Strings.isNotEmpty(loginBody.getUsername())){
             authenticationToken = new UsernamePasswordAuthenticationToken(loginBody.getUsername(),loginBody.getPassword());
        }else if(Strings.isNotEmpty(loginBody.getEmail())){
            authenticationToken = new UsernamePasswordAuthenticationToken(loginBody.getEmail(),loginBody.getPassword());
        }

        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //认证没通过，给出对应的提示
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("登录失败");
        }
        //认证通过，使用userid生成jwt，jwt存入Result作为返回信息
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getUserId().toString();
        String token = jwtUtil.generateToken(loginUser, userId);

        //把完整的用户信息存入redis，userid作为key
        redisCache.setCacheObject(RedisPrefix.LOGIN_USER_PRE + userId,loginUser,jwtUtil.getExpiration(), TimeUnit.MINUTES);

        List<Menu> menus = menuService.selectMenuTreeByUserId(loginUser.getUser().getUserId());
        List<Router> routers = menuService.buildMenus(menus);
        redisCache.setCacheObject(RedisPrefix.ROUTERS_PRE + userId,routers,jwtUtil.getExpiration(), TimeUnit.MINUTES);

        Map<String,String> map = new HashMap<>();
        map.put("token",token);
        return new Result(200,"登录成功",map);
    }

    /**
     * 登出操作
     * @return
     */
    @Override
    public Result logout() {
        //获取SecurityContextHolder中的用户id
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getUser().getUserId();
        //删除redis中的用户信息
        redisCache.deleteObject(RedisPrefix.LOGIN_USER_PRE + userId);
        redisCache.deleteObject(RedisPrefix.ROUTERS_PRE + userId);
        return new Result(200,"退出登录成功",null);
    }

    /**
     * 获取验证码
     * @return
     */
    @Override
    public Result getCode() {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 72, 4, 20);
        String imageBase64 = captcha.getImageBase64();
        String code = captcha.getCode();
        String uuid = IdUtil.simpleUUID();
        redisCache.setCacheObject(RedisPrefix.LOGIN_CODE_PRE + uuid,code,2,TimeUnit.MINUTES);
        Map<String,String> map = new HashMap<>();
        map.put("img",imageBase64);
        map.put("uuid",uuid);
        return Result.success(map);
    }

    /**
     * 校验验证码
     *
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String code, String uuid) {
        String verifikey = RedisPrefix.LOGIN_CODE_PRE + uuid;
        String verifyCode = redisCache.getCacheObject(verifikey);
        if(verifyCode == null){
            throw new RuntimeException("验证码已失效");
        }
        if(!code.toLowerCase().equals(verifyCode.toLowerCase())){
            throw new RuntimeException("验证码错误");
        }else{
            redisCache.deleteObject(verifikey);
        }
    }
}
